using Agron.Data.Contracts;
using Agron.Services.Contracts;

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Threading;


namespace Agron.Services.SessionController
{
    /// <summary>
    /// A <see cref="Component"/> that manages the active member list and implements the <see cref="ISessionEvents"/> callback contract.
    /// </summary>
    public class MemberListManager : Component, ISessionEvents
    {
        private readonly SessionMemberCollection _collection;
        private readonly IMemberListController _viewController;

        /// <summary>
        /// Initializes a new instance of the <see cref="MemberListManager"/> class.
        /// </summary>
        public MemberListManager(IMemberListController controller)
        {
            if (null == controller)
                throw new ArgumentNullException("controller");

            this._viewController = controller;
            this._collection = new SessionMemberCollection();
        }

        /// <summary>
        /// Gets a <see cref="ReadOnlyCollection{SessionMember}"/> that contains the current member list.
        /// </summary>
        /// <value>
        /// A <see cref="ReadOnlyCollection{SessionMember}"/> that contains the current member list.
        /// </value>
        public ReadOnlyCollection<SessionMember> Members { get { return this._collection.AsReadOnly(); } }

        /// <summary>
        /// Called when a user joins a session.
        /// </summary>
        /// <param name="isSessionOwner"><c>true</c> if the user that joined is also the session owner; otherwise, false.</param>
        /// <param name="sessionId">The session unique identifier.</param>
        /// <param name="userProfile">A <see cref="UserProfile"/> data contract that represents the User that joined the session.</param>
        /// <param name="timeStamp">A <see cref="DateTime"/> value that signals the moment in which the Join occurred, expressed as the Coordinated Universal Time (UTC).</param>
        /// <param name="internetIPAddress">The internet IP address.</param>
        /// <param name="localIPAddress">The local IP address.</param>
        public void UserJoined(bool isSessionOwner, int sessionId, UserProfile userProfile, DateTime timeStamp, string internetIPAddress, string localIPAddress)
        {
            if (null == userProfile)
                throw new ArgumentNullException("userProfile");

            if (this._collection.Contains(userProfile.UserId))
            {
                // Only update status
                this._collection[userProfile.UserId].IsOnline = true;
            }
            else
            {
                SessionMember sessionMember = new SessionMember();
                sessionMember.DisplayName = userProfile.DisplayName;
                sessionMember.Email = userProfile.Email;
                sessionMember.InternetAddress = internetIPAddress;
                sessionMember.IsOnline = true;
                sessionMember.IsSessionOwner = isSessionOwner;
                sessionMember.LocalAddress = localIPAddress;
                sessionMember.UserId = userProfile.UserId;
                sessionMember.Username = userProfile.Username;

                this._collection.Add(sessionMember);
            }

            this.DataBind();
        }

        /// <summary>
        /// Called when a user leaves a session.
        /// </summary>
        /// <param name="isSessionOwner"><c>true</c> if the user that joined is also the session owner; otherwise, false.</param>
        /// <param name="sessionId">The session unique identifier.</param>
        /// <param name="userId">The user's unique identifier.</param>
        /// <param name="timeStamp">A <see cref="DateTime"/> value that signals the moment in which the Join occurred, expressed as the Coordinated Universal Time (UTC).</param>
        public void UserLeft(bool isSessionOwner, int sessionId, Guid userId, DateTime timeStamp)
        {
            // We want to reflect on the view that the user is offline, so we'll first change the status
            // and later remove it from the collection.
            this._collection[userId].IsOnline = false;
            this.DataBind();

            Thread removeThread = new Thread(new ParameterizedThreadStart(this.RemoveUserFromCollection));
            removeThread.Start(userId);
        }

        /// <summary>
        /// Releases the unmanaged resources used by the <see cref="T:System.ComponentModel.Component"></see> and optionally releases the managed resources.
        /// </summary>
        /// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);

            if (disposing)
                this._collection.Clear();
        }

        private void DataBind()
        {
            if (null != this._viewController)
                this._viewController.DataBind(this.Members);
        }

        private void RemoveUserFromCollection(object data)
        {
            // Suspend this thread for a while.
            Thread.Sleep(15 * 1000); // 15 seconds

            this._collection.RemoveUser((Guid) data);
            this.DataBind();
        }
    }
}
